package com.lagou.edu.factory;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.pojo.Account;
import com.lagou.edu.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author 应癫
 * <p>
 * <p>
 * 代理对象工厂：生成代理对象的
 */
@Service
public class ProxyFactory {

    @Autowired
    private TransactionManager transactionManager;

    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    /**
     * Jdk动态代理
     *
     * @param obj 委托对象
     * @return 代理对象
     */
    public Object getJdkProxy(Object obj) {

        // 获取代理对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //判断 事物注解是否在 类上 在的话 全部开启注解
                        Transactional transactional = obj.getClass().getAnnotation(Transactional.class);
                        if (transactional != null) {
                            return useTransactional(obj, method, args);
                        }
                        //由于jdk动态代理基于接口进行代理 所以我们需要拿到实际需要代理对象的方法 判断是否有注解
                        Method method1 = obj.getClass().getMethod(method.getName(), method.getParameterTypes());
                        transactional = method1.getAnnotation(Transactional.class);
                        if (transactional != null) {
                            //method 上有 事物注解 则 只对该方法 生效
                            return useTransactional(obj, method, args);
                        }
                        System.out.println("没使用事物");
                        return method.invoke(obj, args);
                    }


                });

    }

    //需要开启事物
    private Object useTransactional(Object obj, Method method, Object[] args) throws Exception {
        System.out.println("使用事物");
        Object result = null;
        try {
            // 开启事务(关闭事务的自动提交)
            transactionManager.beginTransaction();

            result = method.invoke(obj, args);

            // 提交事务
            transactionManager.commit();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            // 回滚事务
            transactionManager.rollback();

            // 抛出异常便于上层servlet捕获
            throw e;

        }
    }

    /**
     * 使用cglib动态代理生成代理对象
     *
     * @param obj 委托对象
     * @return
     */
    public Object getCglibProxy(Object obj) {
        return Enhancer.create(obj.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object result = null;
                //判断 事物注解是否在 类上 在的话 全部开启注解
                Transactional transactional = obj.getClass().getAnnotation(Transactional.class);
                if (transactional != null) {
                    return useTransactional(obj, method, objects);
                }
                transactional = method.getAnnotation(Transactional.class);
                if (transactional != null) {
                    //method 上有 事物注解 则 只对该方法 生效
                    return useTransactional(obj, method, objects);
                }
                System.out.println("没使用事物");
                return method.invoke(obj, objects);
            }
        });
    }
}
